package com.lzh.persevere.aop;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSONObject;
import com.lzh.persevere.model.entity.SysOperationLog;
import com.lzh.persevere.service.SysOperationLogService;
import com.lzh.persevere.utils.DateUtil;
import com.lzh.persevere.utils.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 操作日志切面
 *
 * @author luzhiheng
 * @since 2024-08-27
 **/
@Aspect
@Component
@Slf4j
public class LoggerAspect {

    final ThreadLocal<SysOperationLog> logFastThreadLocal = new ThreadLocal<>();

    final ThreadLocal<Date> beginDateLogThreadLocal = new ThreadLocal<>();

    final SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");

    @Resource
    private SysOperationLogService diOperationLogService;

    /**
     * 切入连接点，使用固定 controller层下的所有文件
     */
    @Pointcut(value = "execution(* com.lzh.shijicloudstack.controller..*(..)))")
    public void logPointcut() {
    }

    /**
     * 请求前置通知
     */
    @Before("logPointcut()")
    public void beforeLogger(JoinPoint joinPoint) {
        // 获取请求参数
        String params = Arrays.toString(joinPoint.getArgs());
        System.out.println(joinPoint);
        Map map;
        try {
            map = BeanUtil.copyProperties(joinPoint.getArgs()[0], Map.class);
        } catch (Exception e) {
            map = new HashMap();
        }
        String ip = String.valueOf(map.get("ip"));

        // 当前登录的用户id
        String userId = String.valueOf(SecurityUtils.getUserId());
        // 当前日期
        Date now = new Date();
        beginDateLogThreadLocal.set(now);


        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();

        log.info("请求访问时间: {}", sdf.format(now));

        // 获取请求url
        String requestUrl = request.getRequestURL().toString();
        log.info("请求url: {}", requestUrl);

        // 获取method
        log.info("请求方式: {}", request.getMethod());

        // 获取请求参数
        log.info("请求参数列表: {}", params);

        // 获取操作人ID
        log.info("操作人ID: {}", userId);

        // 验证请求方法是否带有操作日志注解
        Method signature = ((MethodSignature) joinPoint.getSignature()).getMethod();
        com.lzh.persevere.annotation.LzhOperationLog diOperationLog = signature.getAnnotation(com.lzh.persevere.annotation.LzhOperationLog.class);
        if (diOperationLog != null) {

            // 操作日志记录
            SysOperationLog operationLog = new SysOperationLog();
            operationLog.setCreateTime(now);
            operationLog.setOperationModule(diOperationLog.module());
            operationLog.setOperationEvents(diOperationLog.event());
            operationLog.setOperationData(params.length() > 500 ? params.substring(0, 500) : params);
            operationLog.setOperationUrl(request.getRequestURI());
            // 操作人ID
            operationLog.setCreateUserId(userId);
            // IP地址
            operationLog.setOperationIp(ip);
            logFastThreadLocal.set(operationLog);
        }
    }

    /**
     * 请求后置通知，请求完成会进入到这个方法
     *
     * @param result 响应结果json
     */
    @AfterReturning(value = "logPointcut()", returning = "result")
    public void afterReturningLogger(Object result) {
        Date endDate = new Date();
        Date beginDate = beginDateLogThreadLocal.get();
        long requestTimeConsume = new DateUtil().subMillisecondsByDate(beginDate, endDate);
        beginDateLogThreadLocal.remove();

        // 保存操作日志
        SysOperationLog operationLog = logFastThreadLocal.get();
        if (operationLog != null) {
            operationLog.setOperationStatus(1);
            operationLog.setRequestTimeConsume(requestTimeConsume);

            String str = JSONObject.toJSONString(result);
            operationLog.setOperationResult(str.substring(0, Math.min(str.length(), 500)));

            // 调用具体的 service 保存到数据库中
            diOperationLogService.save(operationLog);

            // 移除本地线程数据
            logFastThreadLocal.remove();
        }

    }

    /**
     * 异常通知，请求异常会进入到这个方法
     */
    @AfterThrowing(value = "logPointcut()", throwing = "throwable")
    public void throwingLogger(Throwable throwable) {
        Date endDate = new Date();
        Date beginDate = beginDateLogThreadLocal.get();
        long requestTimeConsume = new DateUtil().subMillisecondsByDate(beginDate, endDate);

        // 保存操作日志
        SysOperationLog operationLog = logFastThreadLocal.get();
        if (operationLog != null) {
            operationLog.setOperationStatus(0);
            operationLog.setRequestTimeConsume(requestTimeConsume);
            String throwableStr = throwable.toString();
            if (throwableStr.contains(":")) {
                throwableStr = throwableStr.substring(throwableStr.indexOf(":") + 1);
            }

            String operateResult = throwableStr.length() > 500 ? throwableStr.substring(0, 500) : throwableStr;
            operationLog.setOperationResult(operateResult);

            // 调用具体的 service 保存到数据库中
            diOperationLogService.save(operationLog);

            // 移除本地线程数据
            logFastThreadLocal.remove();
        }

    }

}

